package hashTable

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"encoding/binary"
)

//无链表的散列表
type noLinkHashTable struct {
	Buckets  []bucket
	Capacity int32
	Used     int32
}

//桶
type bucket struct {
	HKey string
	Data int
}

func NewNoLinkHashTable(length int32) noLinkHashTable {
	//新建算列表
	ht := noLinkHashTable{}
	ht.Capacity = length
	ht.Buckets = make([]bucket, length)
	return ht
}

func (ht *noLinkHashTable) HSet(key string, value int) {
	bucket := bucket{key, value}
	hashCode := GetHashCode(key, 1)
	index := hashCode % (ht.Capacity - 1)
	ht.addBucket(index, key, bucket)
}

func (ht *noLinkHashTable) HGet(key string) bucket {
	hashCode := GetHashCode(key, 1)
	index := hashCode % (ht.Capacity - 1)
	return ht.findBucket(index, key)
}

func (ht *noLinkHashTable) findBucket(index int32, key string) bucket {
	var empty = bucket{}
	if ok := ht.Buckets[index]; ok != empty {
		if ht.Buckets[index].HKey == key {
			return ht.Buckets[index]
		} else {
			index++
		}
		for {
			if ok := ht.Buckets[index]; ok != empty {
				if ht.Buckets[index].HKey == key {
					return ht.Buckets[index]
				} else {
					index++
				}
			} else {
				return empty
			}
		}
	}
	return empty
}

func (ht *noLinkHashTable) addBucket(index int32, key string, myBucket bucket) {
	if ht.Capacity == ht.Used {
		panic("Table is full")
	}
	var empty = bucket{}
	if ok := ht.Buckets[index]; ok != empty {
		if ok.HKey == key {
			ht.Buckets[index] = myBucket
			return
		}
		//已经存在
		index++
		for {
			//fmt.Println(index)
			if index >= ht.Capacity {
				index = 0
			}
			if ok := ht.Buckets[index]; ok != empty {
				if ok.HKey == key {
					ht.Buckets[index] = myBucket
					return
				}
				index++
			} else {
				ht.Buckets[index] = myBucket
				ht.Used++
				break
			}
		}
	} else {
		ht.Buckets[index] = myBucket
		ht.Used++
	}

}

func GetHashCode(key string, hashType int) int32 {
	var Result []byte
	switch hashType {
	case 1:
		Md5Inst := md5.New()
		Md5Inst.Write([]byte(key))
		Result = Md5Inst.Sum([]byte(""))
	case 2:
		Sha1Inst := sha1.New()
		Sha1Inst.Write([]byte(key))
		Result = Sha1Inst.Sum([]byte(""))
	}
	binBuf := bytes.NewBuffer(Result)
	var x int32
	_ = binary.Read(binBuf, binary.BigEndian, &x)
	return x
}
